1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 package com.sun.media.sound;
27
28 import java.io.ByteArrayOutputStream;
29 import java.io.ByteArrayInputStream;
30 import java.io.DataOutputStream;
31 import java.io.IOException;
32 import java.io.InputStream;
33
34 import java.util.ArrayList;
35 import java.util.List;
36
37 import javax.sound.midi.*;
38
39
40
41
42
43
44
45
46
47
48
49 class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoConnectSequencer {
50
51
52
53
54 private final static boolean DEBUG_PUMP = false;
55 private final static boolean DEBUG_PUMP_ALL = false;
56
57
58
59
60
61 private static final EventDispatcher eventDispatcher;
62
63
64
65
66 static final RealTimeSequencerInfo info = new RealTimeSequencerInfo();
67
68
69 private static Sequencer.SyncMode[] masterSyncModes = { Sequencer.SyncMode.INTERNAL_CLOCK };
70 private static Sequencer.SyncMode[] slaveSyncModes = { Sequencer.SyncMode.NO_SYNC };
71
72 private static Sequencer.SyncMode masterSyncMode = Sequencer.SyncMode.INTERNAL_CLOCK;
73 private static Sequencer.SyncMode slaveSyncMode = Sequencer.SyncMode.NO_SYNC;
74
75
76
77
78
79 private Sequence sequence = null;
80
81
82
83
84
85
86
87 private double cacheTempoMPQ = -1;
88
89
90
91
92
93
94 private float cacheTempoFactor = -1;
95
96
97
98 private boolean[] trackMuted = null;
99
100 private boolean[] trackSolo = null;
101
102
103 private MidiUtils.TempoCache tempoCache = new MidiUtils.TempoCache();
104
105
106
107
108 private boolean running = false;
109
110
111
112 private PlayThread playThread;
113
114
115
116
117
118 private boolean recording = false;
119
120
121
122
123
124 private List recordingTracks = new ArrayList();
125
126
127 private long loopStart = 0;
128 private long loopEnd = -1;
129 private int loopCount = 0;
130
131
132
133
134
135 private ArrayList metaEventListeners = new ArrayList();
136
137
138
139
140
141 private ArrayList controllerEventListeners = new ArrayList();
142
143
144
145 private boolean autoConnect = false;
146
147
148 private boolean doAutoConnectAtNextOpen = false;
149
150
151 Receiver autoConnectedReceiver = null;
152
153
154 static {
155
156 eventDispatcher = new EventDispatcher();
157 eventDispatcher.start();
158 }
159
160
161
162
163 protected RealTimeSequencer() throws MidiUnavailableException {
164 super(info);
165
166 if (Printer.trace) Printer.trace(">> RealTimeSequencer CONSTRUCTOR");
167 if (Printer.trace) Printer.trace("<< RealTimeSequencer CONSTRUCTOR completed");
168 }
169
170
171
172
173 public synchronized void setSequence(Sequence sequence)
174 throws InvalidMidiDataException {
175
176 if (Printer.trace) Printer.trace(">> RealTimeSequencer: setSequence(" + sequence +")");
177
178 if (sequence != this.sequence) {
179 if (this.sequence != null && sequence == null) {
180 setCaches();
181 stop();
182
183 trackMuted = null;
184 trackSolo = null;
185 loopStart = 0;
186 loopEnd = -1;
187 loopCount = 0;
188 if (getDataPump() != null) {
189 getDataPump().setTickPos(0);
190 getDataPump().resetLoopCount();
191 }
192 }
193
194 if (playThread != null) {
195 playThread.setSequence(sequence);
196 }
197
198
199
200 this.sequence = sequence;
201
202 if (sequence != null) {
203 tempoCache.refresh(sequence);
204
205 setTickPosition(0);
206
207 propagateCaches();
208 }
209 }
210 else if (sequence != null) {
211 tempoCache.refresh(sequence);
212 if (playThread != null) {
213 playThread.setSequence(sequence);
214 }
215 }
216
217 if (Printer.trace) Printer.trace("<< RealTimeSequencer: setSequence(" + sequence +") completed");
218 }
219
220
221 public synchronized void setSequence(InputStream stream) throws IOException, InvalidMidiDataException {
222
223 if (Printer.trace) Printer.trace(">> RealTimeSequencer: setSequence(" + stream +")");
224
225 if (stream == null) {
226 setSequence((Sequence) null);
227 return;
228 }
229
230 Sequence seq = MidiSystem.getSequence(stream);
231
232 setSequence(seq);
233
234 if (Printer.trace) Printer.trace("<< RealTimeSequencer: setSequence(" + stream +") completed");
235
236 }
237
238
239 public Sequence getSequence() {
240 return sequence;
241 }
242
243
244 public synchronized void start() {
245 if (Printer.trace) Printer.trace(">> RealTimeSequencer: start()");
246
247
248 if (!isOpen()) {
249 throw new IllegalStateException("sequencer not open");
250 }
251
252
253 if (sequence == null) {
254 throw new IllegalStateException("sequence not set");
255 }
256
257
258 if (running == true) {
259 return;
260 }
261
262
263 implStart();
264
265 if (Printer.trace) Printer.trace("<< RealTimeSequencer: start() completed");
266 }
267
268
269 public synchronized void stop() {
270 if (Printer.trace) Printer.trace(">> RealTimeSequencer: stop()");
271
272 if (!isOpen()) {
273 throw new IllegalStateException("sequencer not open");
274 }
275 stopRecording();
276
277
278 if (running == false) {
279 if (Printer.trace) Printer.trace("<< RealTimeSequencer: stop() not running!");
280 return;
281 }
282
283
284 implStop();
285
286 if (Printer.trace) Printer.trace("<< RealTimeSequencer: stop() completed");
287 }
288
289
290 public boolean isRunning() {
291 return running;
292 }
293
294
295 public void startRecording() {
296 if (!isOpen()) {
297 throw new IllegalStateException("Sequencer not open");
298 }
299
300 start();
301 recording = true;
302 }
303
304
305 public void stopRecording() {
306 if (!isOpen()) {
307 throw new IllegalStateException("Sequencer not open");
308 }
309 recording = false;
310 }
311
312
313 public boolean isRecording() {
314 return recording;
315 }
316
317
318 public void recordEnable(Track track, int channel) {
319 if (!findTrack(track)) {
320 throw new IllegalArgumentException("Track does not exist in the current sequence");
321 }
322
323 synchronized(recordingTracks) {
324 RecordingTrack rc = RecordingTrack.get(recordingTracks, track);
325 if (rc != null) {
326 rc.channel = channel;
327 } else {
328 recordingTracks.add(new RecordingTrack(track, channel));
329 }
330 }
331
332 }
333
334
335 public void recordDisable(Track track) {
336 synchronized(recordingTracks) {
337 RecordingTrack rc = RecordingTrack.get(recordingTracks, track);
338 if (rc != null) {
339 recordingTracks.remove(rc);
340 }
341 }
342
343 }
344
345
346 private boolean findTrack(Track track) {
347 boolean found = false;
348 if (sequence != null) {
349 Track[] tracks = sequence.getTracks();
350 for (int i = 0; i < tracks.length; i++) {
351 if (track == tracks[i]) {
352 found = true;
353 break;
354 }
355 }
356 }
357 return found;
358 }
359
360
361 public float getTempoInBPM() {
362 if (Printer.trace) Printer.trace(">> RealTimeSequencer: getTempoInBPM() ");
363
364 return (float) MidiUtils.convertTempo(getTempoInMPQ());
365 }
366
367
368 public void setTempoInBPM(float bpm) {
369 if (Printer.trace) Printer.trace(">> RealTimeSequencer: setTempoInBPM() ");
370 if (bpm <= 0) {
371
372 bpm = 1.0f;
373 }
374
375 setTempoInMPQ((float) MidiUtils.convertTempo((double) bpm));
376 }
377
378
379 public float getTempoInMPQ() {
380 if (Printer.trace) Printer.trace(">> RealTimeSequencer: getTempoInMPQ() ");
381
382 if (needCaching()) {
383
384 if (cacheTempoMPQ != -1) {
385 return (float) cacheTempoMPQ;
386 }
387
388 if (sequence != null) {
389 return tempoCache.getTempoMPQAt(getTickPosition());
390 }
391
392
393 return (float) MidiUtils.DEFAULT_TEMPO_MPQ;
394 }
395 return (float)getDataPump().getTempoMPQ();
396 }
397
398
399 public void setTempoInMPQ(float mpq) {
400 if (mpq <= 0) {
401
402 mpq = 1.0f;
403 }
404
405 if (Printer.trace) Printer.trace(">> RealTimeSequencer: setTempoInMPQ() ");
406
407 if (needCaching()) {
408
409 cacheTempoMPQ = mpq;
410 } else {
411
412 getDataPump().setTempoMPQ(mpq);
413
414
415 cacheTempoMPQ = -1;
416 }
417 }
418
419
420 public void setTempoFactor(float factor) {
421 if (factor <= 0) {
422
423 return;
424 }
425
426 if (Printer.trace) Printer.trace(">> RealTimeSequencer: setTempoFactor() ");
427
428 if (needCaching()) {
429 cacheTempoFactor = factor;
430 } else {
431 getDataPump().setTempoFactor(factor);
432
433 cacheTempoFactor = -1;
434 }
435 }
436
437
438 public float getTempoFactor() {
439 if (Printer.trace) Printer.trace(">> RealTimeSequencer: getTempoFactor() ");
440
441 if (needCaching()) {
442 if (cacheTempoFactor != -1) {
443 return cacheTempoFactor;
444 }
445 return 1.0f;
446 }
447 return getDataPump().getTempoFactor();
448 }
449
450
451 public long getTickLength() {
452 if (Printer.trace) Printer.trace(">> RealTimeSequencer: getTickLength() ");
453
454 if (sequence == null) {
455 return 0;
456 }
457
458 return sequence.getTickLength();
459 }
460
461
462 public synchronized long getTickPosition() {
463 if (Printer.trace) Printer.trace(">> RealTimeSequencer: getTickPosition() ");
464
465 if (getDataPump() == null || sequence == null) {
466 return 0;
467 }
468
469 return getDataPump().getTickPos();
470 }
471
472
473 public synchronized void setTickPosition(long tick) {
474 if (tick < 0) {
475
476 return;
477 }
478
479 if (Printer.trace) Printer.trace(">> RealTimeSequencer: setTickPosition("+tick+") ");
480
481 if (getDataPump() == null) {
482 if (tick != 0) {
483
484 }
485 }
486 else if (sequence == null) {
487 if (tick != 0) {
488
489 }
490 } else {
491 getDataPump().setTickPos(tick);
492 }
493 }
494
495
496 public long getMicrosecondLength() {
497 if (Printer.trace) Printer.trace(">> RealTimeSequencer: getMicrosecondLength() ");
498
499 if (sequence == null) {
500 return 0;
501 }
502
503 return sequence.getMicrosecondLength();
504 }
505
506
507 public long getMicrosecondPosition() {
508 if (Printer.trace) Printer.trace(">> RealTimeSequencer: getMicrosecondPosition() ");
509
510 if (getDataPump() == null || sequence == null) {
511 return 0;
512 }
513 synchronized (tempoCache) {
514 return MidiUtils.tick2microsecond(sequence, getDataPump().getTickPos(), tempoCache);
515 }
516 }
517
518
519 public void setMicrosecondPosition(long microseconds) {
520 if (microseconds < 0) {
521
522 return;
523 }
524
525 if (Printer.trace) Printer.trace(">> RealTimeSequencer: setMicrosecondPosition("+microseconds+") ");
526
527 if (getDataPump() == null) {
528 if (microseconds != 0) {
529
530 }
531 }
532 else if (sequence == null) {
533 if (microseconds != 0) {
534
535 }
536 } else {
537 synchronized(tempoCache) {
538 setTickPosition(MidiUtils.microsecond2tick(sequence, microseconds, tempoCache));
539 }
540 }
541 }
542
543
544 public void setMasterSyncMode(Sequencer.SyncMode sync) {
545
546 }
547
548
549 public Sequencer.SyncMode getMasterSyncMode() {
550 return masterSyncMode;
551 }
552
553
554 public Sequencer.SyncMode[] getMasterSyncModes() {
555 Sequencer.SyncMode[] returnedModes = new Sequencer.SyncMode[masterSyncModes.length];
556 System.arraycopy(masterSyncModes, 0, returnedModes, 0, masterSyncModes.length);
557 return returnedModes;
558 }
559
560
561 public void setSlaveSyncMode(Sequencer.SyncMode sync) {
562
563 }
564
565
566 public Sequencer.SyncMode getSlaveSyncMode() {
567 return slaveSyncMode;
568 }
569
570
571 public Sequencer.SyncMode[] getSlaveSyncModes() {
572 Sequencer.SyncMode[] returnedModes = new Sequencer.SyncMode[slaveSyncModes.length];
573 System.arraycopy(slaveSyncModes, 0, returnedModes, 0, slaveSyncModes.length);
574 return returnedModes;
575 }
576
577 protected int getTrackCount() {
578 Sequence seq = getSequence();
579 if (seq != null) {
580
581 return sequence.getTracks().length;
582 }
583 return 0;
584 }
585
586
587
588 public synchronized void setTrackMute(int track, boolean mute) {
589 int trackCount = getTrackCount();
590 if (track < 0 || track >= getTrackCount()) return;
591 trackMuted = ensureBoolArraySize(trackMuted, trackCount);
592 trackMuted[track] = mute;
593 if (getDataPump() != null) {
594 getDataPump().muteSoloChanged();
595 }
596 }
597
598
599 public synchronized boolean getTrackMute(int track) {
600 if (track < 0 || track >= getTrackCount()) return false;
601 if (trackMuted == null || trackMuted.length <= track) return false;
602 return trackMuted[track];
603 }
604
605
606 public synchronized void setTrackSolo(int track, boolean solo) {
607 int trackCount = getTrackCount();
608 if (track < 0 || track >= getTrackCount()) return;
609 trackSolo = ensureBoolArraySize(trackSolo, trackCount);
610 trackSolo[track] = solo;
611 if (getDataPump() != null) {
612 getDataPump().muteSoloChanged();
613 }
614 }
615
616
617 public synchronized boolean getTrackSolo(int track) {
618 if (track < 0 || track >= getTrackCount()) return false;
619 if (trackSolo == null || trackSolo.length <= track) return false;
620 return trackSolo[track];
621 }
622
623
624 public boolean addMetaEventListener(MetaEventListener listener) {
625 synchronized(metaEventListeners) {
626 if (! metaEventListeners.contains(listener)) {
627
628 metaEventListeners.add(listener);
629 }
630 return true;
631 }
632 }
633
634
635 public void removeMetaEventListener(MetaEventListener listener) {
636 synchronized(metaEventListeners) {
637 int index = metaEventListeners.indexOf(listener);
638 if (index >= 0) {
639 metaEventListeners.remove(index);
640 }
641 }
642 }
643
644
645 public int[] addControllerEventListener(ControllerEventListener listener, int[] controllers) {
646 synchronized(controllerEventListeners) {
647
648
649
650 ControllerListElement cve = null;
651 boolean flag = false;
652 for(int i=0; i < controllerEventListeners.size(); i++) {
653
654 cve = (ControllerListElement) controllerEventListeners.get(i);
655
656 if (cve.listener.equals(listener)) {
657 cve.addControllers(controllers);
658 flag = true;
659 break;
660 }
661 }
662 if (!flag) {
663 cve = new ControllerListElement(listener, controllers);
664 controllerEventListeners.add(cve);
665 }
666
667
668 return cve.getControllers();
669 }
670 }
671
672
673 public int[] removeControllerEventListener(ControllerEventListener listener, int[] controllers) {
674 synchronized(controllerEventListeners) {
675 ControllerListElement cve = null;
676 boolean flag = false;
677 for (int i=0; i < controllerEventListeners.size(); i++) {
678 cve = (ControllerListElement) controllerEventListeners.get(i);
679 if (cve.listener.equals(listener)) {
680 cve.removeControllers(controllers);
681 flag = true;
682 break;
683 }
684 }
685 if (!flag) {
686 return new int[0];
687 }
688 if (controllers == null) {
689 int index = controllerEventListeners.indexOf(cve);
690 if (index >= 0) {
691 controllerEventListeners.remove(index);
692 }
693 return new int[0];
694 }
695 return cve.getControllers();
696 }
697 }
698
699
700
701
702 public void setLoopStartPoint(long tick) {
703 if ((tick > getTickLength())
704 || ((loopEnd != -1) && (tick > loopEnd))
705 || (tick < 0)) {
706 throw new IllegalArgumentException("invalid loop start point: "+tick);
707 }
708 loopStart = tick;
709 }
710
711 public long getLoopStartPoint() {
712 return loopStart;
713 }
714
715 public void setLoopEndPoint(long tick) {
716 if ((tick > getTickLength())
717 || ((loopStart > tick) && (tick != -1))
718 || (tick < -1)) {
719 throw new IllegalArgumentException("invalid loop end point: "+tick);
720 }
721 loopEnd = tick;
722 }
723
724 public long getLoopEndPoint() {
725 return loopEnd;
726 }
727
728 public void setLoopCount(int count) {
729 if (count != LOOP_CONTINUOUSLY
730 && count < 0) {
731 throw new IllegalArgumentException("illegal value for loop count: "+count);
732 }
733 loopCount = count;
734 if (getDataPump() != null) {
735 getDataPump().resetLoopCount();
736 }
737 }
738
739 public int getLoopCount() {
740 return loopCount;
741 }
742
743
744
745
746
747
748 protected void implOpen() throws MidiUnavailableException {
749 if (Printer.trace) Printer.trace(">> RealTimeSequencer: implOpen()");
750
751
752
753
754 playThread = new PlayThread();
755
756
757
758
759
760 if (sequence != null) {
761 playThread.setSequence(sequence);
762 }
763
764
765 propagateCaches();
766
767 if (doAutoConnectAtNextOpen) {
768 doAutoConnect();
769 }
770 if (Printer.trace) Printer.trace("<< RealTimeSequencer: implOpen() succeeded");
771 }
772
773 private void doAutoConnect() {
774 if (Printer.trace) Printer.trace(">> RealTimeSequencer: doAutoConnect()");
775 Receiver rec = null;
776
777
778
779
780 try {
781 Synthesizer synth = MidiSystem.getSynthesizer();
782 if (synth instanceof ReferenceCountingDevice) {
783 rec = ((ReferenceCountingDevice) synth).getReceiverReferenceCounting();
784 } else {
785 synth.open();
786 try {
787 rec = synth.getReceiver();
788 } finally {
789
790 if (rec == null) {
791 synth.close();
792 }
793 }
794 }
795 } catch (Exception e) {
796
797 }
798 if (rec == null) {
799
800 try {
801 rec = MidiSystem.getReceiver();
802 } catch (Exception e) {
803
804 }
805 }
806 if (rec != null) {
807 autoConnectedReceiver = rec;
808 try {
809 getTransmitter().setReceiver(rec);
810 } catch (Exception e) {}
811 }
812 if (Printer.trace) Printer.trace("<< RealTimeSequencer: doAutoConnect() succeeded");
813 }
814
815 private synchronized void propagateCaches() {
816
817 if (sequence != null && isOpen()) {
818 if (cacheTempoFactor != -1) {
819 setTempoFactor(cacheTempoFactor);
820 }
821 if (cacheTempoMPQ == -1) {
822 setTempoInMPQ((new MidiUtils.TempoCache(sequence)).getTempoMPQAt(getTickPosition()));
823 } else {
824 setTempoInMPQ((float) cacheTempoMPQ);
825 }
826 }
827 }
828
829
830 private synchronized void setCaches() {
831 cacheTempoFactor = getTempoFactor();
832 cacheTempoMPQ = getTempoInMPQ();
833 }
834
835
836
837 protected synchronized void implClose() {
838 if (Printer.trace) Printer.trace(">> RealTimeSequencer: implClose() ");
839
840 if (playThread == null) {
841 if (Printer.err) Printer.err("RealTimeSequencer.implClose() called, but playThread not instanciated!");
842 } else {
843
844 playThread.close();
845 playThread = null;
846 }
847
848 super.implClose();
849
850 sequence = null;
851 running = false;
852 cacheTempoMPQ = -1;
853 cacheTempoFactor = -1;
854 trackMuted = null;
855 trackSolo = null;
856 loopStart = 0;
857 loopEnd = -1;
858 loopCount = 0;
859
860
861
862
863 doAutoConnectAtNextOpen = autoConnect;
864
865 if (autoConnectedReceiver != null) {
866 try {
867 autoConnectedReceiver.close();
868 } catch (Exception e) {}
869 autoConnectedReceiver = null;
870 }
871
872 if (Printer.trace) Printer.trace("<< RealTimeSequencer: implClose() completed");
873 }
874
875 protected void implStart() {
876 if (Printer.trace) Printer.trace(">> RealTimeSequencer: implStart()");
877
878 if (playThread == null) {
879 if (Printer.err) Printer.err("RealTimeSequencer.implStart() called, but playThread not instanciated!");
880 return;
881 }
882
883 tempoCache.refresh(sequence);
884 if (!running) {
885 running = true;
886 playThread.start();
887 }
888 if (Printer.trace) Printer.trace("<< RealTimeSequencer: implStart() completed");
889 }
890
891
892 protected void implStop() {
893 if (Printer.trace) Printer.trace(">> RealTimeSequencer: implStop()");
894
895 if (playThread == null) {
896 if (Printer.err) Printer.err("RealTimeSequencer.implStop() called, but playThread not instanciated!");
897 return;
898 }
899
900 recording = false;
901 if (running) {
902 running = false;
903 playThread.stop();
904 }
905 if (Printer.trace) Printer.trace("<< RealTimeSequencer: implStop() completed");
906 }
907
908
909
910
911
912
913 protected void sendMetaEvents(MidiMessage message) {
914 if (metaEventListeners.size() == 0) return;
915
916
917 eventDispatcher.sendAudioEvents(message, metaEventListeners);
918 }
919
920
921
922
923 protected void sendControllerEvents(MidiMessage message) {
924 int size = controllerEventListeners.size();
925 if (size == 0) return;
926
927
928
929 if (! (message instanceof ShortMessage)) {
930 if (Printer.debug) Printer.debug("sendControllerEvents: message is NOT instanceof ShortMessage!");
931 return;
932 }
933 ShortMessage msg = (ShortMessage) message;
934 int controller = msg.getData1();
935 List sendToListeners = new ArrayList();
936 for (int i = 0; i < size; i++) {
937 ControllerListElement cve = (ControllerListElement) controllerEventListeners.get(i);
938 for(int j = 0; j < cve.controllers.length; j++) {
939 if (cve.controllers[j] == controller) {
940 sendToListeners.add(cve.listener);
941 break;
942 }
943 }
944 }
945 eventDispatcher.sendAudioEvents(message, sendToListeners);
946 }
947
948
949
950 private boolean needCaching() {
951 return !isOpen() || (sequence == null) || (playThread == null);
952 }
953
954
955
956
957
958
959
960 private DataPump getDataPump() {
961 if (playThread != null) {
962 return playThread.getDataPump();
963 }
964 return null;
965 }
966
967 private MidiUtils.TempoCache getTempoCache() {
968 return tempoCache;
969 }
970
971 private static boolean[] ensureBoolArraySize(boolean[] array, int desiredSize) {
972 if (array == null) {
973 return new boolean[desiredSize];
974 }
975 if (array.length < desiredSize) {
976 boolean[] newArray = new boolean[desiredSize];
977 System.arraycopy(array, 0, newArray, 0, array.length);
978 return newArray;
979 }
980 return array;
981 }
982
983
984
985
986 protected boolean hasReceivers() {
987 return true;
988 }
989
990
991 protected Receiver createReceiver() throws MidiUnavailableException {
992 return new SequencerReceiver();
993 }
994
995
996 protected boolean hasTransmitters() {
997 return true;
998 }
999
1000
1001 protected Transmitter createTransmitter() throws MidiUnavailableException {
1002 return new SequencerTransmitter();
1003 }
1004
1005
1006
1007 public void setAutoConnect(Receiver autoConnectedReceiver) {
1008 this.autoConnect = (autoConnectedReceiver != null);
1009 this.autoConnectedReceiver = autoConnectedReceiver;
1010 }
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020 private class SequencerTransmitter extends BasicTransmitter {
1021 private SequencerTransmitter() {
1022 super();
1023 }
1024 }
1025
1026
1027 class SequencerReceiver extends AbstractReceiver {
1028
1029 protected void implSend(MidiMessage message, long timeStamp) {
1030 if (recording) {
1031 long tickPos = 0;
1032
1033
1034 if (timeStamp < 0) {
1035 tickPos = getTickPosition();
1036 } else {
1037 synchronized(tempoCache) {
1038 tickPos = MidiUtils.microsecond2tick(sequence, timeStamp, tempoCache);
1039 }
1040 }
1041
1042
1043 Track track = null;
1044
1045
1046 if (message.getLength() > 1) {
1047 if (message instanceof ShortMessage) {
1048 ShortMessage sm = (ShortMessage) message;
1049
1050 if ((sm.getStatus() & 0xF0) != 0xF0) {
1051 track = RecordingTrack.get(recordingTracks, sm.getChannel());
1052 }
1053 } else {
1054
1055
1056 track = RecordingTrack.get(recordingTracks, -1);
1057 }
1058 if (track != null) {
1059
1060 if (message instanceof ShortMessage) {
1061 message = new FastShortMessage((ShortMessage) message);
1062 } else {
1063 message = (MidiMessage) message.clone();
1064 }
1065
1066
1067 MidiEvent me = new MidiEvent(message, tickPos);
1068 track.add(me);
1069 }
1070 }
1071 }
1072 }
1073 }
1074
1075
1076 private static class RealTimeSequencerInfo extends MidiDevice.Info {
1077
1078 private static final String name = "Real Time Sequencer";
1079 private static final String vendor = "Oracle Corporation";
1080 private static final String description = "Software sequencer";
1081 private static final String version = "Version 1.0";
1082
1083 private RealTimeSequencerInfo() {
1084 super(name, vendor, description, version);
1085 }
1086 }
1087
1088
1089 private class ControllerListElement {
1090
1091
1092
1093
1094 int [] controllers;
1095 ControllerEventListener listener;
1096
1097 private ControllerListElement(ControllerEventListener listener, int[] controllers) {
1098
1099 this.listener = listener;
1100 if (controllers == null) {
1101 controllers = new int[128];
1102 for (int i = 0; i < 128; i++) {
1103 controllers[i] = i;
1104 }
1105 }
1106 this.controllers = controllers;
1107 }
1108
1109 private void addControllers(int[] c) {
1110
1111 if (c==null) {
1112 controllers = new int[128];
1113 for (int i = 0; i < 128; i++) {
1114 controllers[i] = i;
1115 }
1116 return;
1117 }
1118 int temp[] = new int[ controllers.length + c.length ];
1119 int elements;
1120
1121
1122 for(int i=0; i<controllers.length; i++) {
1123 temp[i] = controllers[i];
1124 }
1125 elements = controllers.length;
1126
1127 for(int i=0; i<c.length; i++) {
1128 boolean flag = false;
1129
1130 for(int j=0; j<controllers.length; j++) {
1131 if (c[i] == controllers[j]) {
1132 flag = true;
1133 break;
1134 }
1135 }
1136 if (!flag) {
1137 temp[elements++] = c[i];
1138 }
1139 }
1140
1141 int newc[] = new int[ elements ];
1142 for(int i=0; i<elements; i++){
1143 newc[i] = temp[i];
1144 }
1145 controllers = newc;
1146 }
1147
1148 private void removeControllers(int[] c) {
1149
1150 if (c==null) {
1151 controllers = new int[0];
1152 } else {
1153 int temp[] = new int[ controllers.length ];
1154 int elements = 0;
1155
1156
1157 for(int i=0; i<controllers.length; i++){
1158 boolean flag = false;
1159 for(int j=0; j<c.length; j++) {
1160 if (controllers[i] == c[j]) {
1161 flag = true;
1162 break;
1163 }
1164 }
1165 if (!flag){
1166 temp[elements++] = controllers[i];
1167 }
1168 }
1169
1170 int newc[] = new int[ elements ];
1171 for(int i=0; i<elements; i++) {
1172 newc[i] = temp[i];
1173 }
1174 controllers = newc;
1175
1176 }
1177 }
1178
1179 private int[] getControllers() {
1180
1181
1182
1183 if (controllers == null) {
1184 return null;
1185 }
1186
1187 int c[] = new int[controllers.length];
1188
1189 for(int i=0; i<controllers.length; i++){
1190 c[i] = controllers[i];
1191 }
1192 return c;
1193 }
1194
1195 }
1196
1197
1198 static class RecordingTrack {
1199
1200 private Track track;
1201 private int channel;
1202
1203 RecordingTrack(Track track, int channel) {
1204 this.track = track;
1205 this.channel = channel;
1206 }
1207
1208 static RecordingTrack get(List recordingTracks, Track track) {
1209
1210 synchronized(recordingTracks) {
1211 int size = recordingTracks.size();
1212
1213 for (int i = 0; i < size; i++) {
1214 RecordingTrack current = (RecordingTrack)recordingTracks.get(i);
1215 if (current.track == track) {
1216 return current;
1217 }
1218 }
1219 }
1220 return null;
1221 }
1222
1223 static Track get(List recordingTracks, int channel) {
1224
1225 synchronized(recordingTracks) {
1226 int size = recordingTracks.size();
1227 for (int i = 0; i < size; i++) {
1228 RecordingTrack current = (RecordingTrack)recordingTracks.get(i);
1229 if ((current.channel == channel) || (current.channel == -1)) {
1230 return current.track;
1231 }
1232 }
1233 }
1234 return null;
1235
1236 }
1237 }
1238
1239
1240 class PlayThread implements Runnable {
1241 private Thread thread;
1242 private Object lock = new Object();
1243
1244
1245 boolean interrupted = false;
1246 boolean isPumping = false;
1247
1248 private DataPump dataPump = new DataPump();
1249
1250
1251 PlayThread() {
1252
1253 int priority = Thread.NORM_PRIORITY
1254 + ((Thread.MAX_PRIORITY - Thread.NORM_PRIORITY) * 3) / 4;
1255 thread = JSSecurityManager.createThread(this,
1256 "Java Sound Sequencer",
1257 false,
1258 priority,
1259 true);
1260 }
1261
1262 DataPump getDataPump() {
1263 return dataPump;
1264 }
1265
1266 synchronized void setSequence(Sequence seq) {
1267 dataPump.setSequence(seq);
1268 }
1269
1270
1271
1272 synchronized void start() {
1273
1274 running = true;
1275
1276 if (!dataPump.hasCachedTempo()) {
1277 long tickPos = getTickPosition();
1278 dataPump.setTempoMPQ(tempoCache.getTempoMPQAt(tickPos));
1279 }
1280 dataPump.checkPointMillis = 0;
1281 dataPump.clearNoteOnCache();
1282 dataPump.needReindex = true;
1283
1284 dataPump.resetLoopCount();
1285
1286
1287 synchronized(lock) {
1288 lock.notifyAll();
1289 }
1290
1291 if (Printer.debug) Printer.debug(" ->Started MIDI play thread");
1292
1293 }
1294
1295
1296 synchronized void stop() {
1297 playThreadImplStop();
1298 long t = System.nanoTime() / 1000000l;
1299 while (isPumping) {
1300 synchronized(lock) {
1301 try {
1302 lock.wait(2000);
1303 } catch (InterruptedException ie) {
1304
1305 }
1306 }
1307
1308 if ((System.nanoTime()/1000000l) - t > 1900) {
1309 if (Printer.err) Printer.err("Waited more than 2 seconds in RealTimeSequencer.PlayThread.stop()!");
1310
1311 }
1312 }
1313 }
1314
1315 void playThreadImplStop() {
1316
1317 running = false;
1318 synchronized(lock) {
1319 lock.notifyAll();
1320 }
1321 }
1322
1323 void close() {
1324 Thread oldThread = null;
1325 synchronized (this) {
1326
1327 interrupted = true;
1328 oldThread = thread;
1329 thread = null;
1330 }
1331 if (oldThread != null) {
1332
1333 synchronized(lock) {
1334 lock.notifyAll();
1335 }
1336 }
1337
1338
1339 if (oldThread != null) {
1340 try {
1341 oldThread.join(2000);
1342 } catch (InterruptedException ie) {}
1343 }
1344 }
1345
1346
1347
1348
1349
1350
1351
1352
1353 public void run() {
1354
1355 while (!interrupted) {
1356 boolean EOM = false;
1357 boolean wasRunning = running;
1358 isPumping = !interrupted && running;
1359 while (!EOM && !interrupted && running) {
1360 EOM = dataPump.pump();
1361
1362 try {
1363 Thread.sleep(1);
1364 } catch (InterruptedException ie) {
1365
1366 }
1367 }
1368 if (Printer.debug) {
1369 Printer.debug("Exited main pump loop because: ");
1370 if (EOM) Printer.debug(" -> EOM is reached");
1371 if (!running) Printer.debug(" -> running was set to false");
1372 if (interrupted) Printer.debug(" -> interrupted was set to true");
1373 }
1374
1375 playThreadImplStop();
1376 if (wasRunning) {
1377 dataPump.notesOff(true);
1378 }
1379 if (EOM) {
1380 dataPump.setTickPos(sequence.getTickLength());
1381
1382
1383 MetaMessage message = new MetaMessage();
1384 try{
1385 message.setMessage(MidiUtils.META_END_OF_TRACK_TYPE, new byte[0], 0);
1386 } catch(InvalidMidiDataException e1) {}
1387 sendMetaEvents(message);
1388 }
1389 synchronized (lock) {
1390 isPumping = false;
1391
1392 lock.notifyAll();
1393 while (!running && !interrupted) {
1394 try {
1395 lock.wait();
1396 } catch (Exception ex) {}
1397 }
1398 }
1399 }
1400 if (Printer.debug) Printer.debug("end of play thread");
1401 }
1402 }
1403
1404
1405
1406
1407
1408
1409 private class DataPump {
1410 private float currTempo;
1411 private float tempoFactor;
1412 private float inverseTempoFactor;
1413 private long ignoreTempoEventAt;
1414 private int resolution;
1415 private float divisionType;
1416 private long checkPointMillis;
1417 private long checkPointTick;
1418 private int[] noteOnCache;
1419 private Track[] tracks;
1420 private boolean[] trackDisabled;
1421 private int[] trackReadPos;
1422 private long lastTick;
1423 private boolean needReindex = false;
1424 private int currLoopCounter = 0;
1425
1426
1427
1428
1429
1430 DataPump() {
1431 init();
1432 }
1433
1434 synchronized void init() {
1435 ignoreTempoEventAt = -1;
1436 tempoFactor = 1.0f;
1437 inverseTempoFactor = 1.0f;
1438 noteOnCache = new int[128];
1439 tracks = null;
1440 trackDisabled = null;
1441 }
1442
1443 synchronized void setTickPos(long tickPos) {
1444 long oldLastTick = tickPos;
1445 lastTick = tickPos;
1446 if (running) {
1447 notesOff(false);
1448 }
1449 if (running || tickPos > 0) {
1450
1451 chaseEvents(oldLastTick, tickPos);
1452 } else {
1453 needReindex = true;
1454 }
1455 if (!hasCachedTempo()) {
1456 setTempoMPQ(getTempoCache().getTempoMPQAt(lastTick, currTempo));
1457
1458 ignoreTempoEventAt = -1;
1459 }
1460
1461 checkPointMillis = 0;
1462 }
1463
1464 long getTickPos() {
1465 return lastTick;
1466 }
1467
1468
1469 boolean hasCachedTempo() {
1470 if (ignoreTempoEventAt != lastTick) {
1471 ignoreTempoEventAt = -1;
1472 }
1473 return ignoreTempoEventAt >= 0;
1474 }
1475
1476
1477 synchronized void setTempoMPQ(float tempoMPQ) {
1478 if (tempoMPQ > 0 && tempoMPQ != currTempo) {
1479 ignoreTempoEventAt = lastTick;
1480 this.currTempo = tempoMPQ;
1481
1482 checkPointMillis = 0;
1483 }
1484 }
1485
1486 float getTempoMPQ() {
1487 return currTempo;
1488 }
1489
1490 synchronized void setTempoFactor(float factor) {
1491 if (factor > 0 && factor != this.tempoFactor) {
1492 tempoFactor = factor;
1493 inverseTempoFactor = 1.0f / factor;
1494
1495 checkPointMillis = 0;
1496 }
1497 }
1498
1499 float getTempoFactor() {
1500 return tempoFactor;
1501 }
1502
1503 synchronized void muteSoloChanged() {
1504 boolean[] newDisabled = makeDisabledArray();
1505 if (running) {
1506 applyDisabledTracks(trackDisabled, newDisabled);
1507 }
1508 trackDisabled = newDisabled;
1509 }
1510
1511
1512
1513 synchronized void setSequence(Sequence seq) {
1514 if (seq == null) {
1515 init();
1516 return;
1517 }
1518 tracks = seq.getTracks();
1519 muteSoloChanged();
1520 resolution = seq.getResolution();
1521 divisionType = seq.getDivisionType();
1522 trackReadPos = new int[tracks.length];
1523
1524 checkPointMillis = 0;
1525 needReindex = true;
1526 }
1527
1528 synchronized void resetLoopCount() {
1529 currLoopCounter = loopCount;
1530 }
1531
1532 void clearNoteOnCache() {
1533 for (int i = 0; i < 128; i++) {
1534 noteOnCache[i] = 0;
1535 }
1536 }
1537
1538 void notesOff(boolean doControllers) {
1539 int done = 0;
1540 for (int ch=0; ch<16; ch++) {
1541 int channelMask = (1<<ch);
1542 for (int i=0; i<128; i++) {
1543 if ((noteOnCache[i] & channelMask) != 0) {
1544 noteOnCache[i] ^= channelMask;
1545
1546 getTransmitterList().sendMessage((ShortMessage.NOTE_ON | ch) | (i<<8), -1);
1547 done++;
1548 }
1549 }
1550
1551 getTransmitterList().sendMessage((ShortMessage.CONTROL_CHANGE | ch) | (123<<8), -1);
1552
1553 getTransmitterList().sendMessage((ShortMessage.CONTROL_CHANGE | ch) | (64<<8), -1);
1554 if (doControllers) {
1555
1556 getTransmitterList().sendMessage((ShortMessage.CONTROL_CHANGE | ch) | (121<<8), -1);
1557 done++;
1558 }
1559 }
1560 if (DEBUG_PUMP) Printer.println(" noteOff: sent "+done+" messages.");
1561 }
1562
1563
1564 private boolean[] makeDisabledArray() {
1565 if (tracks == null) {
1566 return null;
1567 }
1568 boolean[] newTrackDisabled = new boolean[tracks.length];
1569 boolean[] solo;
1570 boolean[] mute;
1571 synchronized(RealTimeSequencer.this) {
1572 mute = trackMuted;
1573 solo = trackSolo;
1574 }
1575
1576 boolean hasSolo = false;
1577 if (solo != null) {
1578 for (int i = 0; i < solo.length; i++) {
1579 if (solo[i]) {
1580 hasSolo = true;
1581 break;
1582 }
1583 }
1584 }
1585 if (hasSolo) {
1586
1587 for (int i = 0; i < newTrackDisabled.length; i++) {
1588 newTrackDisabled[i] = (i >= solo.length) || (!solo[i]);
1589 }
1590 } else {
1591
1592 for (int i = 0; i < newTrackDisabled.length; i++) {
1593 newTrackDisabled[i] = (mute != null) && (i < mute.length) && (mute[i]);
1594 }
1595 }
1596 return newTrackDisabled;
1597 }
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607 private void sendNoteOffIfOn(Track track, long endTick) {
1608 int size = track.size();
1609 int done = 0;
1610 try {
1611 for (int i = 0; i < size; i++) {
1612 MidiEvent event = track.get(i);
1613 if (event.getTick() > endTick) break;
1614 MidiMessage msg = event.getMessage();
1615 int status = msg.getStatus();
1616 int len = msg.getLength();
1617 if (len == 3 && ((status & 0xF0) == ShortMessage.NOTE_ON)) {
1618 int note = -1;
1619 if (msg instanceof ShortMessage) {
1620 ShortMessage smsg = (ShortMessage) msg;
1621 if (smsg.getData2() > 0) {
1622
1623 note = smsg.getData1();
1624 }
1625 } else {
1626 byte[] data = msg.getMessage();
1627 if ((data[2] & 0x7F) > 0) {
1628
1629 note = data[1] & 0x7F;
1630 }
1631 }
1632 if (note >= 0) {
1633 int bit = 1<<(status & 0x0F);
1634 if ((noteOnCache[note] & bit) != 0) {
1635
1636 getTransmitterList().sendMessage(status | (note<<8), -1);
1637
1638 noteOnCache[note] &= (0xFFFF ^ bit);
1639 done++;
1640 }
1641 }
1642 }
1643 }
1644 } catch (ArrayIndexOutOfBoundsException aioobe) {
1645
1646
1647 }
1648 if (DEBUG_PUMP) Printer.println(" sendNoteOffIfOn: sent "+done+" messages.");
1649 }
1650
1651
1652
1653
1654
1655
1656
1657 private void applyDisabledTracks(boolean[] oldDisabled, boolean[] newDisabled) {
1658 byte[][] tempArray = null;
1659 synchronized(RealTimeSequencer.this) {
1660 for (int i = 0; i < newDisabled.length; i++) {
1661 if (((oldDisabled == null)
1662 || (i >= oldDisabled.length)
1663 || !oldDisabled[i])
1664 && newDisabled[i]) {
1665
1666
1667
1668
1669 if (tracks.length > i) {
1670 sendNoteOffIfOn(tracks[i], lastTick);
1671 }
1672 }
1673 else if ((oldDisabled != null)
1674 && (i < oldDisabled.length)
1675 && oldDisabled[i]
1676 && !newDisabled[i]) {
1677
1678
1679 if (tempArray == null) {
1680 tempArray = new byte[128][16];
1681 }
1682 chaseTrackEvents(i, 0, lastTick, true, tempArray);
1683 }
1684 }
1685 }
1686 }
1687
1688
1689
1690
1691
1692
1693
1694
1695 private void chaseTrackEvents(int trackNum,
1696 long startTick,
1697 long endTick,
1698 boolean doReindex,
1699 byte[][] tempArray) {
1700 if (startTick > endTick) {
1701
1702 startTick = 0;
1703 }
1704 byte[] progs = new byte[16];
1705
1706 for (int ch = 0; ch < 16; ch++) {
1707 progs[ch] = -1;
1708 for (int co = 0; co < 128; co++) {
1709 tempArray[co][ch] = -1;
1710 }
1711 }
1712 Track track = tracks[trackNum];
1713 int size = track.size();
1714 try {
1715 for (int i = 0; i < size; i++) {
1716 MidiEvent event = track.get(i);
1717 if (event.getTick() >= endTick) {
1718 if (doReindex && (trackNum < trackReadPos.length)) {
1719 trackReadPos[trackNum] = (i > 0)?(i-1):0;
1720 if (DEBUG_PUMP) Printer.println(" chaseEvents: setting trackReadPos["+trackNum+"] = "+trackReadPos[trackNum]);
1721 }
1722 break;
1723 }
1724 MidiMessage msg = event.getMessage();
1725 int status = msg.getStatus();
1726 int len = msg.getLength();
1727 if (len == 3 && ((status & 0xF0) == ShortMessage.CONTROL_CHANGE)) {
1728 if (msg instanceof ShortMessage) {
1729 ShortMessage smsg = (ShortMessage) msg;
1730 tempArray[smsg.getData1() & 0x7F][status & 0x0F] = (byte) smsg.getData2();
1731 } else {
1732 byte[] data = msg.getMessage();
1733 tempArray[data[1] & 0x7F][status & 0x0F] = data[2];
1734 }
1735 }
1736 if (len == 2 && ((status & 0xF0) == ShortMessage.PROGRAM_CHANGE)) {
1737 if (msg instanceof ShortMessage) {
1738 ShortMessage smsg = (ShortMessage) msg;
1739 progs[status & 0x0F] = (byte) smsg.getData1();
1740 } else {
1741 byte[] data = msg.getMessage();
1742 progs[status & 0x0F] = data[1];
1743 }
1744 }
1745 }
1746 } catch (ArrayIndexOutOfBoundsException aioobe) {
1747
1748
1749 }
1750 int numControllersSent = 0;
1751
1752 for (int ch = 0; ch < 16; ch++) {
1753 for (int co = 0; co < 128; co++) {
1754 byte controllerValue = tempArray[co][ch];
1755 if (controllerValue >= 0) {
1756 int packedMsg = (ShortMessage.CONTROL_CHANGE | ch) | (co<<8) | (controllerValue<<16);
1757 getTransmitterList().sendMessage(packedMsg, -1);
1758 numControllersSent++;
1759 }
1760 }
1761
1762
1763 if (progs[ch] >= 0) {
1764 getTransmitterList().sendMessage((ShortMessage.PROGRAM_CHANGE | ch) | (progs[ch]<<8), -1);
1765 }
1766 if (progs[ch] >= 0 || startTick == 0 || endTick == 0) {
1767
1768 getTransmitterList().sendMessage((ShortMessage.PITCH_BEND | ch) | (0x40 << 16), -1);
1769
1770 getTransmitterList().sendMessage((ShortMessage.CONTROL_CHANGE | ch) | (64 << 8), -1);
1771 }
1772 }
1773 if (DEBUG_PUMP) Printer.println(" chaseTrackEvents track "+trackNum+": sent "+numControllersSent+" controllers.");
1774 }
1775
1776
1777
1778 synchronized void chaseEvents(long startTick, long endTick) {
1779 if (DEBUG_PUMP) Printer.println(">> chaseEvents from tick "+startTick+".."+(endTick-1));
1780 byte[][] tempArray = new byte[128][16];
1781 for (int t = 0; t < tracks.length; t++) {
1782 if ((trackDisabled == null)
1783 || (trackDisabled.length <= t)
1784 || (!trackDisabled[t])) {
1785
1786 chaseTrackEvents(t, startTick, endTick, true, tempArray);
1787 }
1788 }
1789 if (DEBUG_PUMP) Printer.println("<< chaseEvents");
1790 }
1791
1792
1793
1794
1795 private long getCurrentTimeMillis() {
1796 return System.nanoTime() / 1000000l;
1797
1798 }
1799
1800 private long millis2tick(long millis) {
1801 if (divisionType != Sequence.PPQ) {
1802 double dTick = ((((double) millis) * tempoFactor)
1803 * ((double) divisionType)
1804 * ((double) resolution))
1805 / ((double) 1000);
1806 return (long) dTick;
1807 }
1808 return MidiUtils.microsec2ticks(millis * 1000,
1809 currTempo * inverseTempoFactor,
1810 resolution);
1811 }
1812
1813 private long tick2millis(long tick) {
1814 if (divisionType != Sequence.PPQ) {
1815 double dMillis = ((((double) tick) * 1000) /
1816 (tempoFactor * ((double) divisionType) * ((double) resolution)));
1817 return (long) dMillis;
1818 }
1819 return MidiUtils.ticks2microsec(tick,
1820 currTempo * inverseTempoFactor,
1821 resolution) / 1000;
1822 }
1823
1824 private void ReindexTrack(int trackNum, long tick) {
1825 if (trackNum < trackReadPos.length && trackNum < tracks.length) {
1826 trackReadPos[trackNum] = MidiUtils.tick2index(tracks[trackNum], tick);
1827 if (DEBUG_PUMP) Printer.println(" reindexTrack: setting trackReadPos["+trackNum+"] = "+trackReadPos[trackNum]);
1828 }
1829 }
1830
1831
1832 private boolean dispatchMessage(int trackNum, MidiEvent event) {
1833 boolean changesPending = false;
1834 MidiMessage message = event.getMessage();
1835 int msgStatus = message.getStatus();
1836 int msgLen = message.getLength();
1837 if (msgStatus == MetaMessage.META && msgLen >= 2) {
1838
1839
1840
1841
1842
1843
1844 if (trackNum == 0) {
1845 int newTempo = MidiUtils.getTempoMPQ(message);
1846 if (newTempo > 0) {
1847 if (event.getTick() != ignoreTempoEventAt) {
1848 setTempoMPQ(newTempo);
1849 changesPending = true;
1850 }
1851
1852 ignoreTempoEventAt = -1;
1853 }
1854 }
1855
1856 sendMetaEvents(message);
1857
1858 } else {
1859
1860 getTransmitterList().sendMessage(message, -1);
1861
1862 switch (msgStatus & 0xF0) {
1863 case ShortMessage.NOTE_OFF: {
1864
1865 int note = ((ShortMessage) message).getData1() & 0x7F;
1866 noteOnCache[note] &= (0xFFFF ^ (1<<(msgStatus & 0x0F)));
1867 break;
1868 }
1869
1870 case ShortMessage.NOTE_ON: {
1871
1872 ShortMessage smsg = (ShortMessage) message;
1873 int note = smsg.getData1() & 0x7F;
1874 int vel = smsg.getData2() & 0x7F;
1875 if (vel > 0) {
1876
1877 noteOnCache[note] |= 1<<(msgStatus & 0x0F);
1878 } else {
1879
1880 noteOnCache[note] &= (0xFFFF ^ (1<<(msgStatus & 0x0F)));
1881 }
1882 break;
1883 }
1884
1885 case ShortMessage.CONTROL_CHANGE:
1886
1887 sendControllerEvents(message);
1888 break;
1889
1890 }
1891 }
1892 return changesPending;
1893 }
1894
1895
1896
1897
1898
1899 synchronized boolean pump() {
1900 long currMillis;
1901 long targetTick = lastTick;
1902 MidiEvent currEvent;
1903 boolean changesPending = false;
1904 boolean doLoop = false;
1905 boolean EOM = false;
1906
1907 currMillis = getCurrentTimeMillis();
1908 int finishedTracks = 0;
1909 do {
1910 changesPending = false;
1911
1912
1913 if (needReindex) {
1914 if (DEBUG_PUMP) Printer.println("Need to re-index at "+currMillis+" millis. TargetTick="+targetTick);
1915 if (trackReadPos.length < tracks.length) {
1916 trackReadPos = new int[tracks.length];
1917 }
1918 for (int t = 0; t < tracks.length; t++) {
1919 ReindexTrack(t, targetTick);
1920 if (DEBUG_PUMP_ALL) Printer.println(" Setting trackReadPos["+t+"]="+trackReadPos[t]);
1921 }
1922 needReindex = false;
1923 checkPointMillis = 0;
1924 }
1925
1926
1927 if (checkPointMillis == 0) {
1928
1929 currMillis = getCurrentTimeMillis();
1930 checkPointMillis = currMillis;
1931 targetTick = lastTick;
1932 checkPointTick = targetTick;
1933 if (DEBUG_PUMP) Printer.println("New checkpoint to "+currMillis+" millis. "
1934 +"TargetTick="+targetTick
1935 +" new tempo="+MidiUtils.convertTempo(currTempo)+"bpm");
1936 } else {
1937
1938 targetTick = checkPointTick + millis2tick(currMillis - checkPointMillis);
1939 if (DEBUG_PUMP_ALL) Printer.println("targetTick = "+targetTick+" at "+currMillis+" millis");
1940 if ((loopEnd != -1)
1941 && ((loopCount > 0 && currLoopCounter > 0)
1942 || (loopCount == LOOP_CONTINUOUSLY))) {
1943 if (lastTick <= loopEnd && targetTick >= loopEnd) {
1944
1945
1946 targetTick = loopEnd - 1;
1947 doLoop = true;
1948 if (DEBUG_PUMP) Printer.println("set doLoop to true. lastTick="+lastTick
1949 +" targetTick="+targetTick
1950 +" loopEnd="+loopEnd
1951 +" jumping to loopStart="+loopStart
1952 +" new currLoopCounter="+currLoopCounter);
1953 if (DEBUG_PUMP) Printer.println(" currMillis="+currMillis
1954 +" checkPointMillis="+checkPointMillis
1955 +" checkPointTick="+checkPointTick);
1956
1957 }
1958 }
1959 lastTick = targetTick;
1960 }
1961
1962 finishedTracks = 0;
1963
1964 for (int t = 0; t < tracks.length; t++) {
1965 try {
1966 boolean disabled = trackDisabled[t];
1967 Track thisTrack = tracks[t];
1968 int readPos = trackReadPos[t];
1969 int size = thisTrack.size();
1970
1971 while (!changesPending && (readPos < size)
1972 && (currEvent = thisTrack.get(readPos)).getTick() <= targetTick) {
1973
1974 if ((readPos == size -1) && MidiUtils.isMetaEndOfTrack(currEvent.getMessage())) {
1975
1976 readPos = size;
1977 break;
1978 }
1979
1980
1981
1982 readPos++;
1983
1984
1985
1986
1987 if (!disabled ||
1988 ((t == 0) && (MidiUtils.isMetaTempo(currEvent.getMessage())))) {
1989 changesPending = dispatchMessage(t, currEvent);
1990 }
1991 }
1992 if (readPos >= size) {
1993 finishedTracks++;
1994 }
1995 if (DEBUG_PUMP_ALL) {
1996 System.out.print(" pumped track "+t+" ("+size+" events) "
1997 +" from index: "+trackReadPos[t]
1998 +" to "+(readPos-1));
1999 System.out.print(" -> ticks: ");
2000 if (trackReadPos[t] < size) {
2001 System.out.print(""+(thisTrack.get(trackReadPos[t]).getTick()));
2002 } else {
2003 System.out.print("EOT");
2004 }
2005 System.out.print(" to ");
2006 if (readPos < size) {
2007 System.out.print(""+(thisTrack.get(readPos-1).getTick()));
2008 } else {
2009 System.out.print("EOT");
2010 }
2011 System.out.println();
2012 }
2013 trackReadPos[t] = readPos;
2014 } catch(Exception e) {
2015 if (Printer.debug) Printer.debug("Exception in Sequencer pump!");
2016 if (Printer.debug) e.printStackTrace();
2017 if (e instanceof ArrayIndexOutOfBoundsException) {
2018 needReindex = true;
2019 changesPending = true;
2020 }
2021 }
2022 if (changesPending) {
2023 break;
2024 }
2025 }
2026 EOM = (finishedTracks == tracks.length);
2027 if (doLoop
2028 || ( ((loopCount > 0 && currLoopCounter > 0)
2029 || (loopCount == LOOP_CONTINUOUSLY))
2030 && !changesPending
2031 && (loopEnd == -1)
2032 && EOM)) {
2033
2034 long oldCheckPointMillis = checkPointMillis;
2035 long loopEndTick = loopEnd;
2036 if (loopEndTick == -1) {
2037 loopEndTick = lastTick;
2038 }
2039
2040
2041 if (loopCount != LOOP_CONTINUOUSLY) {
2042 currLoopCounter--;
2043 }
2044 if (DEBUG_PUMP) Printer.println("Execute loop: lastTick="+lastTick
2045 +" loopEnd="+loopEnd
2046 +" jumping to loopStart="+loopStart
2047 +" new currLoopCounter="+currLoopCounter);
2048 setTickPos(loopStart);
2049
2050
2051
2052
2053
2054
2055
2056
2057 checkPointMillis = oldCheckPointMillis + tick2millis(loopEndTick - checkPointTick);
2058 checkPointTick = loopStart;
2059 if (DEBUG_PUMP) Printer.println(" Setting currMillis="+currMillis
2060 +" new checkPointMillis="+checkPointMillis
2061 +" new checkPointTick="+checkPointTick);
2062
2063 needReindex = false;
2064 changesPending = false;
2065
2066 doLoop = false;
2067 EOM = false;
2068 }
2069 } while (changesPending);
2070
2071 return EOM;
2072 }
2073
2074 }
2075
2076 }